﻿using System;
using System.IO;

namespace ArgusLib
{
	/// <summary>
	/// Source: http://tools.ietf.org/html/rfc2083#page-95
	/// </summary>
	public class CRC32
	{
		uint[] table;

		private uint currentCRC;
		public int CurrentCRC
		{
			get
			{
				uint x = 0xffffffff;
				return (int)(this.currentCRC ^ x);
			}
		}

		private CRC32()
		{
			this.table = new uint[256];
			this.Clear();
		}

		public static CRC32 CreateTable()
		{
			CRC32 RetVal = new CRC32();
			for (int n = 0; n < RetVal.table.Length; n++)
			{
				uint c = (uint)n;
				for (int k = 0; k < 8; k++)
				{
					if ((c & 1) == 1)
						c = 0xedb88320 ^ (c >> 1);
					else
						c = c >> 1;
				}
				RetVal.table[n] = c;
			}
			return RetVal;
		}

		private uint Update(uint crc, byte[] data)
		{
			return this.Update(crc, data, 0, data.Length);
		}

		private uint Update(uint crc, byte b)
		{
			return this.table[(crc ^ (uint)b) & 0xFF] ^ (crc >> 8);
		}

		private uint Update(uint crc, byte[] data, int offset, int count)
		{
			for (int n = offset; n < count; n++)
			{
				crc = this.Update(crc, data[n]);
			}
			return crc;
		}

		private uint Update(uint crc, Stream stream, int count)
		{
			for (int n = 0; n < count; n++)
			{
				int b = stream.ReadByte();
				if (b < 0)
					throw new EndOfStreamException();
				crc = this.Update(crc, (byte)b);
			}
			return crc;
		}

		public void Add(Stream stream, int count)
		{
			this.currentCRC = this.Update(this.currentCRC, stream, count);
		}

		public void Add(byte b)
		{
			this.currentCRC = this.Update(this.currentCRC, b);
		}

		public void Add(byte[] data)
		{
			this.currentCRC = this.Update(this.currentCRC, data);
		}

		public void Add(byte[] data, int offset, int count)
		{
			this.currentCRC = this.Update(this.currentCRC, data, offset, count);
		}

		public void Clear()
		{
			this.currentCRC = 0xffffffff;
		}

		public int Calculate(byte[] data)
		{
			return this.Calculate(data, 0, data.Length);
		}

		public int Calculate(byte[] data, int offset, int count)
		{
			uint crc = 0xffffffff;
			return (int)(this.Update(crc, data, offset, count) ^ crc);
		}

		public int Calculate(Stream stream, int count)
		{
			uint crc = 0xffffffff;
			return (int)(this.Update(crc, stream, count) ^ crc);
		}
	}
}
